package 代码随想录_动态规划.买卖股票;

/**
 * @author zx
 * @create 2022-06-02 16:00
 * ############关键在于至多买卖两次,这意味着可以买卖一次,可以买卖两次,也可以不买卖.##################################
 * 组成部分一：确定状态
 *               最后一步：
 *               子问题：
 *               确定dp数组(dp table)以及下标的含义
 *               一天一共就有五个状态，
 *               0. 没有操作
 *               1. 第一次买入
 *               2. 第一次卖出
 *               3. 第二次买入
 *               4. 第二次卖出
 *               dp[i][j]中i表示第i天,j为[0 - 4]五个状态,dp[i][j]表示第i天状态j所剩最大现金.
 * 组成部分一：确定状态
 * 最后一步：
 * 子问题：
 * 组成部分二：转移方程
 * 需要注意：dp[i][1],表示的是第i天,买入股票的状态,并不是说一定要第i天买入股票,这是容易陷入的误区.
 * 达到dp[i][1]状态，有两个具体操作：
 *      操作一：第i天买入股票了,那么dp[i][1] = dp[i-1][0] - prices[i]
 *      操作二：第i天没有操作,而是沿用前一天买入的状态，即：dp[i][1] = dp[i - 1][1]
 * 那么dp[i][1]究竟选 dp[i-1][0] - prices[i],还是dp[i - 1][1]呢？
 * 一定是选最大的,所以 dp[i][1] = max(dp[i-1][0] - prices[i], dp[i - 1][1]);
 * 同理dp[i][2]也有两个操作：
 *      操作一：第i天卖出股票了,那么dp[i][2] = dp[i - 1][1] + prices[i]
 *      操作二：第i天没有操作,沿用前一天卖出股票的状态,即：dp[i][2] = dp[i - 1][2]
 * 所以dp[i][2] = max(dp[i - 1][1] + prices[i], dp[i - 1][2])
 * 同理可推出剩下状态部分：
 * dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
 * dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
 * 组成部分三：初始条件和边界情况
 *      第0天没有操作,这个最容易想到,就是0,即：dp[0][0] = 0;
 *      第0天做第一次买入的操作,dp[0][1] = -prices[0];
 *      第0天做第一次卖出的操作,这个初始值应该是多少呢？
 *      首先卖出的操作一定是收获利润,整个股票买卖最差情况也就是没有盈利即全程无操作现金为0
 *      从递推公式中可以看出每次是取最大值,那么既然是收获利润如果比0还小了就没有必要收获这个利润了
 *      所以dp[0][2] = 0;
 *      第0天第二次买入操作,初始值应该是多少呢？
 *      不用管第几次,现在手头上没有现金,只要买入,现金就做相应的减少
 *      所以第二次买入操作,初始化为：dp[0][3] = -prices[0];
 *      同理第二次卖出初始化dp[0][4] = 0;
 * 组成部分四：计算顺序
 * 从前向后遍历,因为dp[i],依靠dp[i - 1]的数值。
 */
public class 买卖股票的最佳时机III_123 {
    /**
     * @return 这道题记住五个状态,记住如何推到;其实不难
     */
    public int maxProfit2(int[] prices) {
        int[][] dp = new int[prices.length][5];
        dp[0][0] = 0;         //0. 没有操作
        dp[0][1] = -prices[0];//1. 第一次买入
        dp[0][2] = 0;         //2. 第一次卖出
        dp[0][3] = -prices[0];//3. 第二次买入
        dp[0][4] = 0;         //4. 第二次卖出
        for(int i = 1;i < prices.length;i++){
            dp[i][0] = dp[i - 1][0];
            //昨天就是此状态,今天才是此状态(一定是由前一个状态推导而来)
            dp[i][1] = Math.max(dp[i - 1][1],dp[i - 1][0] - prices[i]);
            dp[i][2] = Math.max(dp[i - 1][2],dp[i - 1][1] + prices[i]);
            dp[i][3] = Math.max(dp[i - 1][3],dp[i - 1][2] - prices[i]);
            dp[i][4] = Math.max(dp[i - 1][4],dp[i - 1][3] + prices[i]);
        }
        return dp[prices.length - 1][4];
    }

    /**
     * @return 一维优化
     *
     * 时间复杂度：O(n)   空间复杂度：O(1)
     */
    public int maxProfit(int[] prices) {
        int[] dp = new int[5];
        dp[0] = 0;
        dp[1] = -prices[0];
        dp[2] = 0;
        dp[3] = -prices[0];
        dp[4] = 0;
        for(int i = 1;i < prices.length;i++) {
            dp[0] = dp[0];
            dp[1] = Math.max(dp[1], dp[0] - prices[i]);
            dp[2] = Math.max(dp[2], dp[1] + prices[i]);
            dp[3] = Math.max(dp[3], dp[2] - prices[i]);
            dp[4] = Math.max(dp[4], dp[3] + prices[i]);
        }
        return dp[4];
    }
}
